/*
 * Copyright © 2004-2014 chenYuan. All rights reserved.
 * @Website:wwww.jspx.net
 * @Mail:39793751@qq.com
 * @author: chenYuan , 陈原
 * @License: Jspx.net Framework Code is open source (LGPL)，Jspx.net Framework 使用LGPL 开源授权协议发布。
 * @jvm:jdk1.6+  x86/amd64
 *
 */
package com.jspx.txweb.config;

import com.jspx.boot.EnvFactory;
import org.slf4j.Logger;
import com.jspx.boot.environment.Environment;
import com.jspx.boot.environment.EnvironmentTemplate;
import org.slf4j.LoggerFactory;
import com.jspx.sioc.BeanFactory;
import com.jspx.sioc.IocContext;
import com.jspx.sioc.Sioc;
import com.jspx.sioc.tag.BeanElement;
import com.jspx.txweb.WebConfigManager;
import com.jspx.txweb.annotation.HttpMethod;
import com.jspx.txweb.annotation.Operate;
import com.jspx.txweb.env.TXWeb;
import com.jspx.txweb.table.OperateVO;
import com.jspx.txweb.util.TXWebUtil;
import com.jspx.txweb.view.OperateComparator;
import com.jspx.utils.*;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;


/**
 * Created by IntelliJ IDEA.
 * User:chenYuan (mail:39793751@qq.com)
 * Date: 2007-1-5
 * Time: 15:50:50
 */
public class TXWebConfigManager implements WebConfigManager {
    final private static Logger log = LoggerFactory.getLogger(TXWebConfigManager.class);
    private final static WebConfigManager instance = new TXWebConfigManager();
    private static final Map<String, ActionConfigBean> restFullPath = new HashMap<String, ActionConfigBean>();

    final private Map<String, Map<String, ActionConfigBean>> configTable = new ConcurrentHashMap<String, Map<String, ActionConfigBean>>();
    final private Map<String, String> extendMap = new HashMap<String, String>();
    final private Map<String, List<DefaultInterceptorBean>> defaultInterceptorMap = new HashMap<String, List<DefaultInterceptorBean>>();
    final private Map<String, List<DefaultUrlInterceptorBean>> defaultUrlInterceptorMap = new HashMap<String, List<DefaultUrlInterceptorBean>>();
    final private Map<String, List<ResultConfigBean>> defaultResultMap = new HashMap<String, List<ResultConfigBean>>();
    final private Map<String, List<OperateVO>> operateVOCache = new HashMap<String, List<OperateVO>>();
    final private Configuration configuration = DefaultConfiguration.getInstance();
    private long noteTime = 0;
    private long reloadTime = DateUtil.DAY;

    //读取锁 begin
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock readLock = lock.readLock(); // 读锁
    private final Lock writeLock = lock.writeLock(); // 读锁
    //读取锁 end

    public static WebConfigManager getInstance() {
        return instance;
    }

    private TXWebConfigManager() {

    }

    /**
     * 从新载入配置文件
     */
     public void clear() {
        readLock.lock();
        try{
            if (configTable.isEmpty()) return;
            configTable.clear();
            defaultInterceptorMap.clear();
            defaultUrlInterceptorMap.clear();
            defaultResultMap.clear();
            operateVOCache.clear();
            restFullPath.clear();
            extendMap.clear();
            //权限资源也从新载入
            if (log.isDebugEnabled())
                log.debug("TXWeb config clear");
            noteTime = System.currentTimeMillis();
        } finally {
            readLock.unlock();
        }
    }

    /**
     * @return 得到配置列表
     */
    public Map<String, Map<String, ActionConfigBean>> getConfigTable() throws Exception {
        checkLoad();
        return configTable;
    }


    /**
     *  检查师傅已经载入
     * @throws Exception 异常
     */
    private void checkLoad() throws Exception {
        writeLock.lock();
        try {
            if (!configTable.isEmpty()) return;
            EnvironmentTemplate envTemplate = EnvFactory.getEnvironmentTemplate();
            reloadTime = envTemplate.getLong(Environment.TXWebConfigReload, DateUtil.DAY);
            if (log.isDebugEnabled()) log.debug("TXWeb config reload " + reloadTime + " millisecond");
            //1.初始化所有配置动作
            configTable.clear();
            configTable.putAll(configuration.loadConfigMap());

            extendMap.clear();
            extendMap.putAll(configuration.getExtendMap());

            defaultInterceptorMap.clear();
            defaultInterceptorMap.putAll(configuration.getDefaultInterceptorMap());

            defaultUrlInterceptorMap.clear();
            defaultUrlInterceptorMap.putAll(configuration.getDefaultUrlInterceptorMap());

            defaultResultMap.clear();
            defaultResultMap.putAll(configuration.getDefaultResultMap());

            //2.生成restFull路径
            for (String namespace : configTable.keySet()) {
                Map<String, ActionConfigBean> map = configTable.get(namespace);
                if (map != null) for (ActionConfigBean actionConfigBean : map.values()) {
                    if (actionConfigBean==null)
                    {
                        log.error("出现空：ActionConfigBean " + namespace);
                    }  else if (!actionConfigBean.isInit())
                    {
                            initActionConfigBean(actionConfigBean, namespace);
                    }
                }
            }
            //载入IOC配置的action 根据注释生成配置
            iocToActionConfigBean();
        } finally {
            writeLock.unlock();
        }
    }

    /**
     * @param namespace 命名空间
     * @return 得到默认拦截器配置, 默认拦截器不使用继承方式
     */
    @SuppressWarnings("unchecked")
    public List<String> getDefaultInterceptors(String namespace) throws Exception {
        checkLoad();
        String nameK = namespace;
        if (StringUtil.isNULL(nameK)) nameK = TXWeb.global;
        final List<String> result = new LinkedList<String>();
        List<DefaultInterceptorBean> list = defaultInterceptorMap.get(nameK);
        if (list != null) {
            for (DefaultInterceptorBean defaultInterceptorBean : list) {
                result.add(defaultInterceptorBean.getName());
            }
        }

        int x = nameK.lastIndexOf("/");
        if (x > 1) {
            //子扩展
            nameK = nameK.substring(0, x);
            list = defaultInterceptorMap.get(nameK);
            if (list != null && !list.isEmpty())
                for (DefaultInterceptorBean defaultInterceptorBean : list) {
                    if (defaultInterceptorBean.isExtend())
                        result.add(defaultInterceptorBean.getName());
                }
        }
        return result;
    }

    /**
     * @param namespace 命名空间
     * @return 得到页面拦截配置
     */
    @SuppressWarnings("unchecked")
    public List<String> getPageDefaultInterceptors(String namespace) {
        String nameK = namespace;
        if (StringUtil.isNULL(nameK)) nameK = TXWeb.global;
        List<DefaultUrlInterceptorBean> list = defaultUrlInterceptorMap.get(nameK);
        if (list == null || list.isEmpty()) return null;
        List<String> result = new LinkedList<String>();
        for (DefaultUrlInterceptorBean defaultInterceptorBean : list) {
            result.add(defaultInterceptorBean.getName());
        }
        int x = nameK.lastIndexOf("/");
        if (x > 1) {
            //子扩展
            nameK = nameK.substring(0, x);
            list = defaultUrlInterceptorMap.get(nameK);
            if (list != null && !list.isEmpty())
                for (DefaultUrlInterceptorBean defaultInterceptorBean : list) {
                    if (defaultInterceptorBean.isExtend())
                        result.add(defaultInterceptorBean.getName());
                }
        }
        return result;
    }

    /**
     * @return 得到命名空间列表
     */
    public List<String> getNamespaceList() throws Exception {
        checkLoad();
        List<String> result = new ArrayList<String>();
        for (String key : extendMap.keySet()) {
            String v = extendMap.get(key);
            if (!result.contains(key)) result.add(key);
            if (!result.contains(v)) result.add(v);
        }
        return result;
    }

    /**
     * 命名空间第一层表示软件名称
     * @return 得到部署了那些软件
     */
    public List<String> getSoftList() throws Exception {
        checkLoad();
        List<String> result = new ArrayList<String>();
        for (String key : extendMap.keySet()) {
            if (key != null && !key.contains("/") && !result.contains(key) && !"global".equalsIgnoreCase(key))
                result.add(key);
        }
        return result;
    }

    /**
     * @return 得到命名空间继承关系列表
     */
    public Map<String, String> getExtendList() throws Exception {
        checkLoad();
        return extendMap;
    }

    /**
     * @param namespace 命名空间
     * @return 得到默认返回配置
     */
    @SuppressWarnings("unchecked")
    public List<ResultConfigBean> getDefaultResults(String namespace) {
        String nameK = namespace;
        if (StringUtil.isNULL(nameK)) nameK = TXWeb.global;
        List<ResultConfigBean> result = defaultResultMap.get(nameK);
        if (!StringUtil.hasLength(nameK) || (result != null && !result.isEmpty())) return result;
        int x = nameK.lastIndexOf("/");
        if (x > 1) {
            //子扩展
            nameK = nameK.substring(0, x);
            result = defaultResultMap.get(nameK);
        }
        return result;
    }

    /**
     * @param namespace 命名空间列表
     * @return 得到本命名空间下的命名空间
     */
    public List<String> getActionList(String namespace) throws Exception {
        checkLoad();
        String nameK = namespace;
        if (StringUtil.isNULL(nameK)) nameK = TXWeb.global;
        List<String> result = new ArrayList<String>();
        for (String key : configTable.keySet()) {
            if (key.startsWith(nameK))
                result.add(key);
        }
        return result;
    }

    /**
     * 得到配置信息
     * @param namePart  action name
     * @param namespace 命名控件
     * @return 配置
     * @throws IOException 异常
     */
    public ActionConfig getActionConfig(String namePart, String namespace, boolean reload) throws Exception {
        checkLoad();
        if (namePart == null) namePart = "*";
        String nameK = namespace;
        if (StringUtil.isNULL(nameK)) nameK = Sioc.global;
        ActionConfigBean result = null;
        Map<String, ActionConfigBean> elementMap;
        while (!StringUtil.isNULL(nameK) && !Sioc.global.equalsIgnoreCase(nameK)) {
            elementMap = configTable.get(nameK);
            if (elementMap != null) {
                for (String name : elementMap.keySet()) {
                    if (!name.equals("*") && namePart.matches(name)) {
                        return elementMap.get(name);
                    }
                }
                result = elementMap.get("*");
                if (result != null) return result;
            }
            if (extendMap.containsKey(nameK))
                nameK = extendMap.get(nameK);
            else {
                if (nameK.contains("/")) nameK = StringUtil.substringBeforeLast(nameK, "/");
                else nameK = Sioc.global;
            }
        }
        elementMap = configTable.get(Sioc.global);
        if (elementMap != null) {
            for (String name : elementMap.keySet()) {
                if (!name.equals("*") && namePart.matches(name)) {
                    return elementMap.get(name);
                }
            }
            result = elementMap.get("*");
        }
        // 通过RestFull 内置方式得到
        if (result == null) result = getActionConfigBean(namePart, namespace);
        //如果都没有就使用默认的
        if (result == null&&elementMap!=null) result = elementMap.get("*");
        if (reload && result == null)
        {
            if (System.currentTimeMillis() - noteTime > reloadTime)
            {
                clear();
            }
            if (log.isDebugEnabled())
                log.debug("TXWeb config not find ActionConfigBean name:" + namePart + "  namespace: " + namespace);
        }
        return result;
    }

    /**
     * @param namePart  文件名称部分
     * @param namespace 命名空间，也是RESTFull 的路径
     * @return 通过RESTFull 方式得到配置
     * @throws IOException io异常
     */
    private ActionConfigBean getActionConfigBean(String namePart, String namespace) throws IOException {
        String nameK = namespace;
        if (StringUtil.isNULL(nameK)) nameK = TXWeb.global;
        for (String key : restFullPath.keySet()) {
            ActionConfigBean actionConfigBean = restFullPath.get(key);
            if (TXWeb.global.equalsIgnoreCase(nameK) || StringUtil.isNULL(nameK)) {
                if (namePart.equalsIgnoreCase(actionConfigBean.getLocation()) || namePart.matches(actionConfigBean.getLocation()))
                    return actionConfigBean;
            } else if (actionConfigBean.getLocation().contains("/")) {
                String nPart = StringUtil.substringAfterLast(actionConfigBean.getLocation(), "/");
                String namespacePart = TXWebUtil.getNamespace(actionConfigBean.getLocation());
                if (nameK.equalsIgnoreCase(namespacePart) && (nPart.equalsIgnoreCase(namePart) || namePart.matches(nPart))) {
                    return actionConfigBean;
                }
            }
        }
        return null;
    }


    /**
     * 得到某命名空间里边的动作列表,让你能够在开发的过程中得到动作列表来判断权限
     *
     * @param namespace 命名空间
     * @return 动作配置列表
     * @throws Exception 异常
     */
    public Map<String, ActionConfigBean> getActionMap(String namespace) throws Exception {
        checkLoad();
        String nameK = namespace;
        if (StringUtil.isNULL(nameK)) nameK = TXWeb.global;
        Map<String, Map<String, ActionConfigBean>> fullConfigTable = getConfigTable();
        Map<String, ActionConfigBean> elementMap = fullConfigTable.get(nameK);
        //多接一级命名空间方便 动态域名空间方式  begin
        int x = nameK.lastIndexOf("/");
        if (elementMap == null && x > 1) {
            //子扩展
            nameK = nameK.substring(0, x);
            elementMap = fullConfigTable.get(nameK);
        }
        return elementMap;
    }

    /**
     * 初始化actionBean XML配置外的其他信息
     *
     * @param actionConfigBean TXWeb配置对象
     * @param namespace        命名空间
     */
    @SuppressWarnings("unchecked")
    private static void initActionConfigBean(ActionConfigBean actionConfigBean, String namespace) {
        String[] beanTypes = StringUtil.split(actionConfigBean.getType(), StringUtil.SEMICOLON);
        String className = null;
        BeanFactory beanFactory = EnvFactory.getBeanFactory();
        IocContext iocContext = beanFactory.getIocContext();
        if (ArrayUtil.inArray(beanTypes, TXWeb.CONFIG_CLASS, true)) {
            //目的是为了兼容 TXWeb里边直接配置class name
            className = actionConfigBean.getIocBean();
        } else {
            try {
                BeanElement beanElement = iocContext.getBeanElement(actionConfigBean.getIocBean(), namespace);
                if (beanElement!=null)
                    className = beanElement.getClassName();
            } catch (Exception e) {
                e.printStackTrace();
                log.error(actionConfigBean.toString(), e);
            }
        }
        if (className == null)
        {
            log.debug(namespace+"命名空间下不能找到配置:"+actionConfigBean.toString());
            return ;
        }
        Class cla = null;
        try {
            cla = Class.forName(StringUtil.trim(className));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            log.error(className, e);
        }
        actionConfigBean.setClassName(className);


        //外部配置优先
        HttpMethod httpMethod = (HttpMethod) cla.getAnnotation(HttpMethod.class);
        if (httpMethod != null) {
            actionConfigBean.setCaption(StringUtil.isNULL(actionConfigBean.getCaption())?httpMethod.caption():actionConfigBean.getCaption());
            if (!actionConfigBean.isMobile())
                actionConfigBean.setMobile(httpMethod.mobile());
            if (StringUtil.isNULL(actionConfigBean.getLocation()))
                actionConfigBean.setLocation(httpMethod.location());
        }

        Map<String, Operate> operateMap = actionConfigBean.getOperateMap();
        Method[] methods = ClassUtil.getDeclaredMethods(cla);//字段
        for (Method method : methods) {
            Operate operate = method.getAnnotation(Operate.class);
            if (operate != null) operateMap.put(method.getName(), operate);
        }
    }

    /**
     * 载入IOC配置的action 根据注释生成配置
     * @throws Exception 异常
     */
    @SuppressWarnings("unchecked")
    private static void iocToActionConfigBean() throws Exception {
        BeanFactory beanFactory = EnvFactory.getBeanFactory();
        IocContext iocContext = beanFactory.getIocContext();
        List<BeanElement> beanElements = iocContext.getElementList();
        for (BeanElement element : beanElements)
        {
            Class cla = null;
            BeanElement beanElement = null;
            try {
                beanElement = iocContext.getBeanElement(element.getId(), element.getNamespace());
                if (beanElement!=null)
                    cla = Class.forName(StringUtil.trim(beanElement.getClassName()));
            } catch (Exception e) {
                e.printStackTrace();
                log.error("ioc 初始化失败:" + beanElement.getClassName(), e);

            }
            if (cla==null)
            {
                if (log.isDebugEnabled())
                    throw new Exception("ioc 初始化失败:"+element.getId()+"@" + element.getNamespace());
                else
                    log.error("ioc 初始化失败:" + beanElement.getClassName());
            }
            HttpMethod httpMethod = (HttpMethod)cla.getAnnotation(HttpMethod.class);
            if (httpMethod != null) {
                ActionConfigBean actionConfigBean = new ActionConfigBean();
                if (StringUtil.hasLength(httpMethod.location()))
                    actionConfigBean.setActionName(StringUtil.substringAfterLast(httpMethod.location(), "/"));
                actionConfigBean.setIocBean(element.getId());
                actionConfigBean.setClassName(cla.getName());
                actionConfigBean.setCaption(StringUtil.isNULL(actionConfigBean.getCaption())?httpMethod.caption():actionConfigBean.getCaption());
                actionConfigBean.setMobile(httpMethod.mobile());
                actionConfigBean.setLocation(httpMethod.location());
                //载入函数方法 begin
                Map<String, Operate> operateMap = actionConfigBean.getOperateMap();
                Method[] methods = ClassUtil.getDeclaredMethods(cla.getClass());//字段
                for (Method method : methods) {
                    Operate operate = method.getAnnotation(Operate.class);
                    if (operate != null)
                    {
                        operateMap.put(method.getName(), operate);
                    }
                }
                //载入函数方法 end
                restFullPath.put(actionConfigBean.getLocation(), actionConfigBean);
            }
        }
    }

    /**
     * @param namespace 命名空间
     * @return 操作列表
     * @throws Exception 异常
     */
    public List<OperateVO> getOperateForNamespace(String namespace) throws Exception {
        List<OperateVO> result = new ArrayList<OperateVO>();
        Map<String, ActionConfigBean> list = getActionMap(namespace);
        if (list!=null)
        for (String key : list.keySet()) {
            ActionConfigBean actionBean = list.get(key);
            Map<String, Operate> operateMap = actionBean.getOperateMap();
            if (operateMap.isEmpty()) {
                //一个什么都没有的类，保留浏览控制 begin
                OperateVO operateVO = new OperateVO();
                operateVO.setCaption(actionBean.getCaption());
                operateVO.setMethodCaption("浏览");
                operateVO.setClassName(actionBean.getClassName());
                operateVO.setActionName(actionBean.getActionName());
                operateVO.setNamespace(namespace);
                if (StringUtil.isNULL(operateVO.getClassName())) operateVO.setClassName(actionBean.getIocBean());
                operateVO.setClassMethod(TXWebUtil.defaultExecute);
                result.add(operateVO);
                //一个什么都没有的类，保留浏览控制 end
            } else {
                for (String methodName : operateMap.keySet()) {
                    Operate op = operateMap.get(methodName);
                    //名称
                    OperateVO operateVO = new OperateVO();
                    operateVO.setCaption(actionBean.getCaption());
                    operateVO.setMethodCaption(op.caption());
                    operateVO.setActionName(actionBean.getActionName());
                    operateVO.setClassName(actionBean.getClassName());
                    operateVO.setNamespace(namespace);

                    if (StringUtil.isNULL(operateVO.getClassName())) operateVO.setClassName(actionBean.getIocBean());
                    operateVO.setClassMethod(methodName);
                    result.add(operateVO);
                }
                //action    类补充浏览控制,解决继承问题begin
                OperateVO operateVO = new OperateVO();
                operateVO.setCaption(actionBean.getCaption());
                operateVO.setMethodCaption("浏览");
                operateVO.setActionName(actionBean.getActionName());
                operateVO.setClassName(actionBean.getClassName());
                operateVO.setNamespace(namespace);
                if (StringUtil.isNULL(operateVO.getClassName())) operateVO.setClassName(actionBean.getIocBean());
                operateVO.setClassMethod(TXWebUtil.defaultExecute);
                result.add(operateVO);
                //action    类补充浏览控制,解决继承问题end
            }
        }
        return result;
    }

    /**
     * @return 操作列表, 包含继承的动作 /user/xxxx  条件为user
     * @throws Exception 异常
     */
    @SuppressWarnings("unchecked")
    public Collection<OperateVO> getOperateList(String namespace) throws Exception {
        Collection<OperateVO> cols = operateVOCache.get(namespace);
        if (cols != null) return cols;
        List<String> list = new ArrayList<String>();
        list.add(namespace);
        Map<String, String> extend = getExtendList();
        for (String key : extend.keySet()) {
            //这里不能够使用模糊判断
            if (key.startsWith(namespace)&&!list.contains(key)) {
                list.add(key);
            }
        }

        Map<String, OperateVO> operateList = new HashMap<String, OperateVO>();
        for (String space : list)
        {
            List<OperateVO> operates = getOperateForNamespace(space);
            for (OperateVO vo : operates) {
                    operateList.put(vo.getActionMethodId(), vo);
            }
        }
        List<OperateVO> operateSortList = new ArrayList<OperateVO>(operateList.values());
        Comparator comparator = new OperateComparator();
        Collections.sort(operateSortList, comparator);
        operateVOCache.put(namespace, operateSortList);
        return operateSortList;
    }
}